package com.witsight.hibernate.dao.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;

import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.query.Query;
import org.hibernate.query.spi.NamedQueryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.repository.support.PageableExecutionUtils.TotalSupplier;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.witsight.hibernate.dao.AliasToBeanNestedResultTransformer;
import com.witsight.hibernate.dao.JpaCommonRepository;
import com.witsight.hibernate.util.SqlBuilder;
import com.witsight.platform.model.BoEntity;
import com.witsight.platform.model.IBaseObject;
import com.witsight.platform.util.lang.BeanUtil;
import com.witsight.platform.util.lang.CollectionUtil;
import com.witsight.platform.util.lang.MapUtil;
import com.witsight.platform.util.lang.NumberUtil;
import com.witsight.platform.util.lang.ObjectUtil;

import joinery.DataFrame;

/**   
 * 说明：共通服务 可以单独使用，没有ORM的限制
 * @Title: CommonRepositoryImpl.java 
 * @Package com.witsight.hibernate.dao.impl 
 * @See: {@link #CommonRepositoryImpl()}
 * Copyright: Copyright (c) 2017
 * Company:sany huax witsight team by product
 * @author: fuyp  
 * @date: 2017年9月19日 上午8:37:12 
 * @version: V1.0
 *
 */
@Component
public final class JpaCommonRepositoryImpl implements JpaCommonRepository{
	@PersistenceContext
	private EntityManager em;	
	@Autowired
	private EntityManagerFactory entityManagerFactory;
	
	@Override
	public <O> List<O> findSqlAllByParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		Query<O> query = getSqlQueryByNameAndParams(queryName, bparam);
		return query.getResultList();
	}

	@Override
	public <O> O findSqlOneByParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		Query<O> query = getSqlQueryByNameAndParams(queryName, bparam);
		query.setMaxResults(1);
		List<O> list = query.getResultList();
		return list.size()>0?list.get(0):null;
	}

	/**
	 * 
	 * 说明：根据翻页取得所有的对象，通过动态sql
	 * @Title: findSqlPageByParams
	 * @See: {@link #findSqlPageByParams(String, Map, Pageable)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @param pageable 翻页参数
	 * @return 翻页结果集合
	 * @date: 2017年9月15日 上午10:25:47
	 */
	@Override
	public <O> Page<O> findSqlPageByParams(String queryName, IBaseObject params, Pageable pageable) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		Query<O> query = getSqlQueryByNameAndParams(queryName, bparam, pageable);
		return readSqlPage(query, bparam, pageable);
	}
	/**
	 * 
	 * 说明：读取翻页数据
	 * @Title: readSqlPage
	 * @See: {@link #readSqlPage(Query, BoEntity, Pageable)}
	 * @author fuyp
	 * @param query 查询query
	 * @param params 参数
	 * @param pageable 翻页对象
	 * @return 翻页数据
	 * @date: 2017年9月19日 上午8:44:55
	 */
	public <S> Page<S> readSqlPage(Query<S> query, BoEntity params, Pageable pageable) {
		Assert.notNull(query, "query must not be null!");
		Assert.notNull(pageable, "pageable must not be null!");
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());
		return PageableExecutionUtils.getPage(query.getResultList(), pageable, new TotalSupplier() {
			@Override
			public long get() {				
				return executeCountSqlQuery(getCountQuery(query.getQueryString(), params));
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	protected Query<Long> getCountQuery(String originalQuery, BoEntity params) {
		Assert.notNull(originalQuery, "originalQuery must not be null!");
		String countQuery = QueryUtils.createCountQueryFor(originalQuery, "1");		
		// 创建 Query对象
		final Query<Long> query = (Query<Long>)this.em.createNativeQuery(countQuery);
		if (MapUtil.isNotEmpty(params)) {
			query.setProperties(params);
		}
		return query;
	}
	/**
	 * Executes a count query and transparently sums up all values returned.
	 * 
	 * @param query must not be {@literal null}.
	 * @return
	 */
	private static Long executeCountSqlQuery(Query<Long> query) {
		Assert.notNull(query, "NativeQuery must not be null!");

		List<Long> totals = query.getResultList();
		Long total = 0L;

		for (Object element : totals) {
			Long count = NumberUtil.toLong(ObjectUtil.getDisplayString(element), 0L);
			total += count;
		}

		return total;
	}
	/**
	 * 说明：根据 namequery的name和参数 取得对应的queryString
	 * 
	 * @Title: getQueryString
	 * @See: {@link #getSqlQueryByNameAndParams(String, BoEntity)}
	 * @author fuyp
	 * @param queryName 查询的名称
	 * @param params 参数
	 * @return 
	 * @date: 2017年9月14日 下午4:07:31
	 */
	@SuppressWarnings({ "unchecked", "deprecation" })
	private <O> Query<O> getSqlQueryByNameAndParams(String queryName, BoEntity params, Pageable pageable) {
		NamedSQLQueryDefinition namedSQLQueryDefinition = getNamedSQLQuery(queryName);
		Assert.notNull(namedSQLQueryDefinition,"namedSQLQueryDefinition of sql not be found  by " + queryName);		
		String queryString = SqlBuilder.parse(namedSQLQueryDefinition.getQueryString(), params);
		if(pageable!=null) {
			queryString = QueryUtils.applySorting(queryString, pageable.getSort());
		}
		Assert.notNull(queryString,"namedSQLQueryDefinition of sql not be found  by " + queryName);		
		NativeSQLQueryReturn[] queryReturns = namedSQLQueryDefinition.getQueryReturns();
		Class<?> mappingClass = null;
		if (ObjectUtil.isNotEmpty(queryReturns)) {
			for (NativeSQLQueryReturn queryReturn : queryReturns) {
				try {
					if (!(queryReturn instanceof NativeSQLQueryRootReturn)) {
						continue;
					}
					String returnEntityName = ((NativeSQLQueryRootReturn) queryReturn).getReturnEntityName();
					mappingClass = Class.forName(returnEntityName);
					break;
				} catch (ClassNotFoundException e) {
					throw new IllegalArgumentException(e);
				}
			}
		}
		
		if (mappingClass == null) {
			mappingClass = BoEntity.class;
		}
		// 创建 Query对象
		final Query<O> query = (Query<O>) this.em.createNativeQuery(queryString);
		query.setResultTransformer(new AliasToBeanNestedResultTransformer(mappingClass));
		if (MapUtil.isNotEmpty(params)) {
			query.setProperties(params);
		}
		
		// 设置query的属性
		query.setCacheable(namedSQLQueryDefinition.isCacheable());
		query.setCacheMode(namedSQLQueryDefinition.getCacheMode());
		query.setCacheRegion(namedSQLQueryDefinition.getCacheRegion());
		query.setReadOnly(namedSQLQueryDefinition.isReadOnly());
		if (namedSQLQueryDefinition.getLockOptions() != null) {
			query.setLockOptions(namedSQLQueryDefinition.getLockOptions());
		}
		
		if (namedSQLQueryDefinition.getMaxResults() != null) {
			query.setMaxResults(namedSQLQueryDefinition.getMaxResults());
		}
		if (namedSQLQueryDefinition.getTimeout() != null) {
			query.setTimeout(namedSQLQueryDefinition.getTimeout());
		}
		return query;
	}
	/**
	 * 说明：根据 namequery的name和参数 取得对应的queryString
	 * 
	 * @Title: getQueryString
	 * @See: {@link #getSqlQueryByNameAndParams(String, BoEntity)}
	 * @author fuyp
	 * @param queryName 查询的名称
	 * @param params 参数
	 * @return 
	 * @date: 2017年9月14日 下午4:07:31
	 */
	@SuppressWarnings({ "unchecked", "deprecation" })
	private <O> Query<O> getSqlQueryByNameAndParams(String queryName, BoEntity params) {
		return getSqlQueryByNameAndParams(queryName,params,null);
	}
	
	/**
	 * 
	 * 说明：根据key 取得对应的NamedSQLQueryDefinition
	 * @Title: getNamedSQLQuery
	 * @See: {@link #getNamedSQLQuery(String)}
	 * @author fuyp
	 * @param queryName queryName
	 * @return  NamedQueryDefinition
	 * @date: 2017年9月18日 下午4:32:33
	 */
	private NamedSQLQueryDefinition getNamedSQLQuery(String queryName){
		NamedQueryRepository namedQueryRepository = getNamedQueryRepository();
		Assert.notNull(namedQueryRepository, "namedQueryRepository is empty.");
		if (namedQueryRepository == null) {
			return null;
		}
		
		return namedQueryRepository.getNamedSQLQueryDefinition(queryName);
	}
	
	private NamedQueryRepository getNamedQueryRepository() {
		NamedQueryRepository namedQueryRepository = null;
		Object sessionFactory = entityManagerFactory.createEntityManager().getEntityManagerFactory();
		if (sessionFactory instanceof SessionFactoryImpl) {
			namedQueryRepository = ((SessionFactoryImpl) sessionFactory).getNamedQueryRepository();
		}
		
		return namedQueryRepository;
	}
	@Override
	public DataFrame<Object> findDataFramesByParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		Query<BoEntity> query = getSqlQueryByNameAndParams(queryName, bparam);
		List<BoEntity> list= query.getResultList();
		Set<String> columns = new HashSet<String>();
		if(CollectionUtil.isEmpty(list)) {
			columns = list.get(0).keySet();
		}
		DataFrame<Object> dataFrame = new DataFrame<Object>(columns);
		for(BoEntity obj:list) {
			List<Object> rowData = new ArrayList<>();
			columns.forEach(column->{
				rowData.add((Object) obj.get(column));
			});
			dataFrame.append(rowData);
		}
		return dataFrame;
	}
}
